//General Purpose
using System;
using System.Collections.Generic;
using System.Text;

//For the project
using CS6238Project2.Banking.Communication;
using CS6238Project2.Banking.Commerce;
using CS6238Project2.Banking.Tools;

//For crypto
using System.Security.Cryptography.Xml;
using System.ComponentModel;
//For Serialization
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace CS6238Project2.Banking.Entity
{
    /// <summary>
    /// An entity which keeps a record of money, transactions,
    /// and user accounts.
    /// </summary>
    [Serializable]
    public class Bank : Merchant
    {
        private BindingList<Account> accounts;
        private BindingList<AddressEntry> users;
        private int iterate;
        //public b_receiveTransmitter transmitter;
        /// <summary>
        /// Default constructor instantiates a blank record.
        /// It initializes some list members to facilitate the Grid view implementation.
        /// 
        /// </summary>
        public Bank()
        {
            
            iterate = 0;
            accounts = new BindingList<Account>();
            accounts.Add(new Account("Bank of America", 0));
            accounts[0].Record.Add( new Transaction
                ("Bank of America", "Bank of America", 0, 10000000, DateTime.Now));
            //new b_receiveTransmitter(this);

        }


        

        /// <summary>
        /// Processes an order received from a user/merchant
        /// </summary>
        /// <param name="received">The parcel that was received</param>
        /// <returns>A parcel to be sent or analyzed</returns>
        new public Parcel ProcessParcel(Parcel received)
        {
            //Check to make sure receiver and itself are the same
            try
            {

                if ((received.Receiver != name) && (received.Type != "Dual Transaction"))
                {
                    received.MessageData = "Parcel receiver was " + received.Receiver +
                                          ", but should be " + name;
                    received.Type = "Invalid Parcel";
                }
                //Recieved a transaction, process it
                if (received.Type == "Transaction")
                {
                    
                    Transact(received);
                    //says whether the transaction was approved
                    //Should be in the message data
                    received.Type = "Transaction Response";
                }
                //Parcel needs to be sent detailing results

                //Received transaction from Merchant purchase
                else if (received.Type == "Dual Transaction")
                {
                    received = DualTransact(received);
                    received.Type = "Dual Transaction Response";
                }
                //Reply with response

                //Receive a request for registration
                else if (received.Type == "Registration Request")
                {
                    //asks to register with bank
                    received = RegisterUser(received);
                    if (received.MessageData == "Approved")
                    {
                        accounts.Add(new Account(received.Receiver, iterate));
                        
                    }
                    received.Account = accounts[accounts.Count - 1];
                    received.Type = "Registration Response";  
                }
                //Tell them if they were registered, include key
                //Note, Key cannot be turned into Xml

                //Receive a request for the record and merchant list
                else if (received.Type == "List Request")
                {
                    received.Account=  Accounts[AcctIndex(received.Number)];
                    received.NameList = merchants;
                    received.Receiver = received.Sender;
                    received.Sender = name;
                    received.Type = "List Response";
                    //maybe add in verification?
                }
                //Return that merchant and record list

                else
                {
                    received.MessageData = "Type " + received.Type + " not recognized";

                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                received.MessageData = "Exception occurred.";
            }


            //Catch the addressing thing...
            if (received.Receiver == name)
            {
                received.Receiver = received.Sender;
                received.Sender = name;
            }
            return received;
        }



        


        /// <summary>
        /// A simple method that fulfils a transaction.
        /// </summary>
        /// <param name="transact">The transaction to be fulfilled.</param>
        private void Transact(Transaction transact)
        {

            int index = AcctIndex(transact.Account);
            try
            {
                //adjust the balance in the account
                accounts[index].Balance += transact.Adjustment;
                //put the transaction on record
                accounts[index].Record.Add(transact);

                //If the balance is red and the transaction is a deduction,
                if ((accounts[index].Balance < 0) && (transact.Adjustment < 0))
                {
                    //then apply overdraft fee
                    accounts[index].Balance += -100;
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
        }

        /// <summary>
        /// Anaylzes a transaction and attempts
        /// </summary>
        /// <param name="parcel">The Parcel containing the transaction</param>
        /// <returns>A parcel signaling approve or disapprove</returns>
        private Parcel Transact(Parcel parcel)
        {
            //Find the account
            Certificate cert = RetrieveCertificate(parcel.Certificate.Subject, false);
            
            if (cert != null)
            {
                //Set the key
                
                parcel.Certificate.Token = cert.Token;
                Transaction tempTransaction = parcel.Certificate.d_Transaction();
                
                //check that the two transactions are equivalent
                if (parcel.Transaction == tempTransaction)
                {
                    //apply the transaction
                    Transact(parcel.Transaction);
                    parcel.MessageData = "Approved";
                }
                else
                {
                    parcel.MessageData = "Transaction data invalid";
                }
                //clear the key
                parcel.Certificate.Token = null;
            }
            else
            {
                parcel.MessageData = "Account not found";
            }
            

            return parcel;
        }

        /// <summary>
        /// Analyzes and attempts to apply a dual transaction
        /// </summary>
        /// <param name="parcel">The parcel to be analyzed</param>
        /// <returns>A parcel to be returned to the sender</returns>
        private Parcel DualTransact(Parcel parcel)
        {
            /*      SEPERATE INTO USER AND MERCHANT TRANSACTIONS        */
            Certificate userCertificate = RetrieveCertificate(parcel.Source, false);
            Certificate merchantCertificate = RetrieveCertificate(parcel.Certificate.Subject, false);
            Certificate parcelCert = new Certificate(parcel.Certificate);
            Certificate authCert;
            Parcel userParcel = new Parcel(parcel);
            Transaction merchantCertTransaction;
            Transaction userCertTransaction = null;
            bool approve = true;

            /*                      MERCHANT                            */
            if (approve)
            {
                parcel.Certificate.Token = merchantCertificate.Token;//Insert the key
                //Confirm the Merchant's transaction and that in the certificate are the same
                merchantCertTransaction = parcel.Certificate.d_Transaction();
                approve = (merchantCertTransaction == parcel.MerchantTransaction) && approve;
            }

            /*                        USER                              */
            
            if (approve)
            {
                userParcel.Certificate.Token = userCertificate.Token;
                authCert = userParcel.d_Authorization(); //should be decrypted by the user/bank key

                
                //Confirm the transaction is approved by the user
                authCert.Token = userCertificate.Token; //set the certificate's key to the user/bank key
                userCertTransaction = authCert.d_Transaction();
                approve = approve && (userCertTransaction == parcel.Transaction);
            }


            /*                    USER AND MERCHANT                     */
            if (approve)
            {
                //assure that the transactions are set for the same time
                approve = approve && (parcel.Transaction.Timestamp == parcel.MerchantTransaction.Timestamp);
                //assure the transactions are equal
                approve = approve && (userCertTransaction.Adjustment == (parcel.MerchantTransaction.Adjustment * -1));
            }

            /*                        PROCESS                           */
            if (approve)
            {
                Transact(userCertTransaction);
                Transact(parcel.MerchantTransaction);
                string sender = parcel.Receiver;
                string receiver = parcel.Sender;
                parcel.Sender = sender;
                parcel.Receiver = receiver;

                parcel.MessageData = "Approved";
            }
            else
            {
                parcel.MessageData = "Transaction not approved";
            }
            return parcel;
        }


        /// <summary>
        /// Registers a user into the bank's database.
        /// </summary>
        /// <param name="parcel">A request from the user to be registered</param>
        /// <returns>A parcel that sends the shared key and approval.</returns>
        new protected Parcel RegisterUser(Parcel parcel)
        {
            parcel.Type = "Registration Response";
            if (RetrieveCertificate(parcel.Sender, false) == null)
            {
                iterate++;
                //First, if they are a merchant then add them to the list

                if (parcel.MessageData == "Merchant")
                {
                    if (merchants == null)
                    {
                        merchants = new BindingList<AddressEntry>();

                    }
                    //save the port/Ip of the merchant
                    AddressEntry entry = new AddressEntry();
                    entry.Port = parcel.SenderPort;
                    entry.IP = parcel.SenderAddr;
                    entry.Name = parcel.Sender;
                    merchants.Add(entry);
                }
                    //Save the port/IP of the user
                else
                {
                    if (users == null)
                    {
                        users = new BindingList<AddressEntry>();
                    }
                    AddressEntry entry = new AddressEntry();
                    entry.Port = parcel.SenderPort;
                    entry.IP = parcel.SenderAddr;
                    entry.Name = parcel.Sender;
                    users.Add(entry);
                }
                Certificate certificate = new Certificate();//create a new cert
                certificate.Token = new Token();//generate a key
                certificate.Issuer = name;//issue it
                certificate.Subject = parcel.Sender; //set the subject to the requester
                parcel.Number = iterate;
                parcel.MessageData = "Approved"; //approve the request
                parcel.Receiver = parcel.Sender;//set the receiver as the requester
                parcel.Sender = name;//set self as sender
                parcel.Certificate = certificate; //set the certificate
                certificates.Add(certificate);//add the certificate to our database
            }
            return parcel;
        }




        /// <summary>
        /// Naive search function to locate the account in question
        /// </summary>
        /// <param name="acctNumber">The number of the account in question</param>
        /// <returns>the index of the account, or -1 if not found</returns>
        private int AcctIndex(int acctNumber)
        {
            int index = -1;
            int i = 0;
            bool found = false;
            while ((i < accounts.Count) && !found)
            {
                if (accounts[i].Number == acctNumber)
                {
                    index = i;
                }
                i++;
            }
            return index;
        }

        /// <summary>
        /// A list containing all the accounts in the bank.
        /// </summary>
        public BindingList<Account> Accounts
        {
            get { return accounts; }
            set { accounts = value; }
        }


        /// <summary>
        /// The list of all users and their locations
        /// </summary>
        public BindingList<AddressEntry> Users
        {
            get { return users; }
            set { users = value; }
        }

        
    }


    
}
